home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / checkbox / test.py < prev    next >
Encoding:
Python Source  |  2009-04-27  |  8.2 KB  |  265 lines

  1. #
  2. # This file is part of Checkbox.
  3. #
  4. # Copyright 2008 Canonical Ltd.
  5. #
  6. # Checkbox is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # Checkbox is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Checkbox.  If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. """
  20. The purpose of this module is to encapsulate the concept of a test
  21. which might be presented in several ways. For example, a test might
  22. require manual intervention whereas another test might be completely
  23. automatic. Either way, this module provides the base common to each type
  24. of test.
  25. """
  26.  
  27. import re
  28. import logging
  29.  
  30. from gettext import gettext as _
  31.  
  32. from checkbox.lib.environ import add_variable, remove_variable
  33. from checkbox.lib.signal import signal_to_name, signal_to_description
  34.  
  35. from checkbox.command import Command
  36. from checkbox.frontend import frontend
  37. from checkbox.requires import Requires
  38.  
  39.  
  40. DESKTOP = "desktop"
  41. LAPTOP = "laptop"
  42. SERVER = "server"
  43. ALL_CATEGORIES = [DESKTOP, LAPTOP, SERVER]
  44.  
  45. I386 = "i386"
  46. AMD64 = "amd64"
  47. LPIA = "lpia"
  48. SPARC = "sparc"
  49. ALL_ARCHITECTURES = [I386, AMD64, LPIA, SPARC]
  50.  
  51. FAIL = "fail"
  52. PASS = "pass"
  53. SKIP = "skip"
  54. ALL_STATUS = [PASS, FAIL, SKIP]
  55.  
  56.  
  57. class TestResult(object):
  58.  
  59.     def __init__(self, test, status, data, duration=None):
  60.         self.test = test
  61.         self.status = status
  62.         self.data = data
  63.         self.duration = duration
  64.  
  65.     @property
  66.     def attributes(self):
  67.         return {
  68.             "status": self.status,
  69.             "data": self.data,
  70.             "duration": self.duration}
  71.  
  72.     @property
  73.     def devices(self):
  74.         return self.test.requires.get_devices()
  75.  
  76.     @property
  77.     def packages(self):
  78.         return self.test.requires.get_packages()
  79.  
  80.     def _get_status(self):
  81.         return self._status
  82.  
  83.     def _set_status(self, status):
  84.         if status not in ALL_STATUS:
  85.             raise Exception, "Invalid status: %s" % status
  86.  
  87.         self._status = status
  88.  
  89.     status = property(_get_status, _set_status)
  90.  
  91.  
  92. class TestCommand(Command):
  93.  
  94.     def __init__(self, test, *args, **kwargs):
  95.         super(TestCommand, self).__init__(test.command, test.timeout,
  96.             *args, **kwargs)
  97.         self.test = test
  98.  
  99.     @frontend("get_test_result")
  100.     def execute(self, *args, **kwargs):
  101.         return super(TestCommand, self).execute(*args, **kwargs)
  102.  
  103.     def post_execute(self, result):
  104.         result = super(TestCommand, self).post_execute(result)
  105.  
  106.         if result.if_exited:
  107.             exit_status = result.exit_status
  108.             if exit_status == 0:
  109.                 status = PASS
  110.                 data = result.stdout
  111.                 if not data:
  112.                     data = result.stderr
  113.             elif exit_status == 127:
  114.                 status = SKIP
  115.                 data = _("Command failed, skipping.")
  116.             else:
  117.                 status = FAIL
  118.                 data = result.stderr
  119.         elif result.if_signaled:
  120.             status = SKIP
  121.             term_signal = result.term_signal
  122.             data = _("Received terminate signal %s: %s") % \
  123.                 (signal_to_name(term_signal),
  124.                  signal_to_description(term_signal))
  125.         else:
  126.             raise Exception, "Command not terminated: %s" \
  127.                 % self.get_command()
  128.  
  129.         duration = result.duration
  130.         return TestResult(self.test, status, data, duration)
  131.  
  132.  
  133. class TestDescription(Command):
  134.  
  135.     def __init__(self, test):
  136.         super(TestDescription, self).__init__(test.description, test.timeout)
  137.         self.test = test
  138.         self.output = None
  139.  
  140.     def get_command(self):
  141.         command = super(TestDescription, self).get_command()
  142.         return "cat <<EOF\n%s\nEOF\n" % command
  143.  
  144.     def pre_execute(self, result=None):
  145.         super(TestDescription, self).pre_execute()
  146.         if re.search(r"\$output", self.get_command()):
  147.             if not self.output and not result:
  148.                 result = self.test.command()
  149.  
  150.             if result:
  151.                 self.output = result.data.strip()
  152.                 result.data = ""
  153.  
  154.             add_variable("output", self.output)
  155.  
  156.     @frontend("get_test_description")
  157.     def execute(self, *args, **kwargs):
  158.         return super(TestDescription, self).execute(*args, **kwargs)
  159.  
  160.     def post_execute(self, result):
  161.         result = super(TestDescription, self).post_execute(result)
  162.         remove_variable("output")
  163.  
  164.         if not result.if_exited \
  165.            or result.exit_status != 0:
  166.             raise Exception, "Description failed: %s" \
  167.                 % self.get_command()
  168.  
  169.         return result.stdout
  170.  
  171.  
  172. class Test(object):
  173.     """
  174.     Test base class which should be inherited by each test
  175.     implementation. A test instance contains the following required
  176.     fields:
  177.  
  178.     name:          Unique name for a test.
  179.     plugin:        Plugin name to handle this test.
  180.     description:   Long description of what the test does.
  181.     suite:         Name of the suite containing this test.
  182.  
  183.     An instance also contains the following optional fields:
  184.  
  185.     architectures: List of architectures for which this test is relevant:
  186.                    amd64, i386, powerpc and/or sparc
  187.     categories:    List of categories for which this test is relevant:
  188.                    desktop, laptop and/or server
  189.     command:       Command to run for the test.
  190.     depends:       List of names on which this test depends. So, if
  191.                    the other test fails, this test will be skipped.
  192.     requires:      Registry expressions which are requirements for
  193.                    this test: 'input.mouse' in info.capabilities
  194.     timeout:       Timeout for running the command.
  195.     user:          User to run the command.
  196.     optional:      Boolean expression set to True if this test is optional
  197.                    or False if this test is required.
  198.     """
  199.  
  200.     required_fields = ["name", "plugin", "description", "suite"]
  201.     optional_fields = {
  202.         "architectures": [],
  203.         "categories": [],
  204.         "command": None,
  205.         "depends": [],
  206.         "requires": None,
  207.         "timeout": None,
  208.         "user": None,
  209.         "optional": False}
  210.  
  211.     def __init__(self, registry, **attributes):
  212.         super(Test, self).__setattr__("attributes", attributes)
  213.  
  214.         # Typed fields
  215.         for field in ["architectures", "categories", "depends"]:
  216.             if attributes.has_key(field):
  217.                 attributes[field] = re.split(r"\s*,\s*", attributes[field])
  218.         for field in ["timeout"]:
  219.             if attributes.has_key(field):
  220.                 attributes[field] = int(attributes[field])
  221.  
  222.         # Optional fields
  223.         for field in self.optional_fields.keys():
  224.             if not attributes.has_key(field):
  225.                 attributes[field] = self.optional_fields[field]
  226.  
  227.         # Requires field
  228.         attributes["requires"] = Requires(registry, attributes["requires"])
  229.  
  230.         # Command field
  231.         attributes["command"] = TestCommand(self)
  232.  
  233.         # Description field
  234.         attributes["description"] = TestDescription(self)
  235.  
  236.         self._validate()
  237.  
  238.     def _validate(self):
  239.         # Unknown fields
  240.         for field in self.attributes.keys():
  241.             if field not in self.required_fields + self.optional_fields.keys():
  242.                 logging.info("Test attributes contains unknown field: %s",
  243.                     field)
  244.                 del self.attributes[field]
  245.  
  246.         # Required fields
  247.         for field in self.required_fields:
  248.             if not self.attributes.has_key(field):
  249.                 raise Exception, \
  250.                     "Test attributes does not contain '%s': %s" \
  251.                     % (field, self.attributes)
  252.  
  253.     def __getattr__(self, name):
  254.         if name not in self.attributes:
  255.             raise AttributeError, name
  256.  
  257.         return self.attributes[name]
  258.  
  259.     def __setattr__(self, name, value):
  260.         if name not in self.attributes:
  261.             raise AttributeError, name
  262.  
  263.         self.attributes[name] = value
  264.         self._validate()
  265.